/*
**  File: AquaDist.c
**
**  This file is part of the AQUA program suite, which is being
**  developed in the NMR Spectroscopy Department, Bijvoet Center for 
**  Biomolecular Research, Utrecht. 
**
**
**  AUTHOR(S) : Ton Rullmann
**  VERSION   : 1.5.0
**  DATE      : Dec 18, 1997
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "Compiler.h"
#include "General.h"

#include "AquaMacros.h"
#include "AquaTypes.h"

/****************  function prototypes  ****************/

#include "AquaFuncts.h"
#include "AquaStrucset.h"
#include "AquaFiles.h"
#include "MenuFuncts.h"
#include "Functs.h"

#include "AquaDist.h"

/****************  globals  ****************/

extern Boolean  batch;                                /* import from Functs */
extern int      terseness;                            /* import from Functs */
char            *program_name = "AquaDist";           /* export to Functs */
char            *program_vers = aqua_version;         /* export to Functs */


/* BEGIN_EXTH */

/****************  main  ****************/
/*
**
*/

int main( int argc, char *argv[] )
{
    char         *fct_name = "AquaDist main";
    Strucset     theSet;
    DistdataList inf_list;
    Distinf      *dist_inf;
    Distinfl     *dist_infl;
    DistinfTyp   ftype_inf = inf_unktyp;
    int          dist_count, s_count;
    FILE         *fpv;
    Qtyp         qtype_inf, qtype_val;
    Filnam       dinf_filnam, drst_filnam, dval_filnam;
    Card         ident;
    int          item;
    char         temp_value[4];

    enum { item_do };
    static MenuItem menulist[] =
    {
        { item_EXEC,  "exec", none, on },
        { item_SET,   "set",  none, on },
        { item_do,    "do",   none, on }, 
        { item_QUIT,  "quit", none, on } 
    };
    static Menu menu =
    {
        "Main menu", "command", "Give command:", 
        sizeof(menulist)/sizeof(MenuItem)
    };
    
    static OptionItem optionlist[] =
    {
        { opt_batch,  "batch",     logical, on }, 
        { opt_terseness, "terseness",   integer, on }, 
        { opt_prtaq,  "print",     logical, on }, 
        { opt_prtres, "printres",  logical, on }
    };
    static Menu options =
    {
        "Options menu", "option", "Give option:", 
        sizeof(optionlist)/sizeof(MenuItem)
    }; 


/* initialize */

    ProgramInit( argc, argv );
    ProjectInit();
    SetMenu( &menu, menulist );
    SetOptions( &options, optionlist );
    sprintf( temp_value, "%d", terseness );
    ModOption( opt_terseness, temp_value );    
    
    inf_list.count = 0;

/* the main menu loop */

    while ( true )
    {
        item = ReadMenuItem();
        switch ( item )
        {
            case item_EXEC:
		DoExec();
                break;
            case item_SET:
		ReadOption();
		batch = OptionTrue( opt_batch );
                if ( !Check_General_Options () ) {
	            printf( "Error in main: general options set are not okay\n" );
	            MError( );
                }
                terseness   = GetOptionIVal ( opt_terseness );                
                break;
            case item_do:
		goto doit;
            case item_QUIT:
            case item_NULL:
                exit( 0 );
                break;
            default:
                ProcessMenuError( menu_fatal );
                break;
        }
    }


/* do the main thing */

doit:

    StrucsetInit( &theSet, "theSet", true );
    
/* process the distance-info file saving the header lines */

    *dinf_filnam = '\0';
    ProcessDistinfFile( &inf_list, &dist_count, &s_count, &qtype_inf, theSet.filnam,
                        drst_filnam, ident, true, &ftype_inf, dinf_filnam );

    if ( ftype_inf == inf_normal || ftype_inf == inf_multi_model ) {
	dist_infl = NULL;
	dist_inf  = (Distinf *) (inf_list.fst_desc->data);
    } else if ( ftype_inf == inf_extended ) { /* special case, cf. ProcessDistinfFile */
	dist_infl = (Distinfl *) (inf_list.fst_desc->data);
	dist_inf  = (Distinf *) (inf_list.lst_desc->data);
    } else {
	FATAL( "Wrong file/data type - Code error" );
        exit(99); /* Will never be executed */
    }

/* open and read the strucset file */

    if ( !ProcessStrucsetFile( &theSet, -1 ) )
	exit( 1 );

    if ( ftype_inf == inf_multi_model && theSet.struc_count != s_count )
	FATAL( "Number of models in strucset differs from distance info file" );

/* initialize the file for the distance values, using the 'ident' of the info file */

    switch ( qtype_inf )
    {
        case noe_inf_data:
            qtype_val = noe_val_data;
            break;
        case noe_infl_data:
            qtype_val = noe_vall_data;
            break;
        case ss_inf_data:
            qtype_val = ss_val_data;
            break;
        case ss_infl_data:
            qtype_val = ss_vall_data;
            break;
        case hb_inf_data:
            qtype_val = hb_val_data;
            break;
        case hb_infl_data:
            qtype_val = hb_vall_data;
            break;
        case dis_inf_data:
            qtype_val = dis_val_data;
            break;
        case dis_infl_data:
            qtype_val = dis_vall_data;
            break;
        default:
            FATAL( "Illegal qtype value for dinf file - program error" );
            exit(99); /* Will never be executed */
    }
    
    if ( terseness < 9 ) {
        puts( "Creating the distance values file" );
    }
    fpv = OpenAquaFile( qtype_val, "w", dval_filnam, true );
    if ( !fpv )
        exit( 1 );
    WriteAquaIDLines( fpv, qtype_val, dval_filnam );

/* write the saved info header lines to the values file */

    WriteTxtBuffer( fpv );

/* write name of info file to values file */

    fprintf( fpv, "$ Info data: %s\n", dinf_filnam );

/* compute and write the distance values */

    if ( ftype_inf == inf_normal || ftype_inf == inf_multi_model )
	MakeDval( fpv, &theSet, dist_inf, dist_count, ftype_inf );
    else if ( ftype_inf == inf_extended )
	MakeDvall( fpv, &theSet, dist_infl, dist_count );
    else
	FATAL( "Wrong file/data type - Code error" );

/* end program */

    exit( 0 );
}

/****************  MakeDval  ****************/
/*
**  Compute and write a (normal) distance values list.
**  The file has already been opened, and the header has been written.
*/

void MakeDval( FILE *fpv, Strucset *setptr, Distinf *dist_inf, 
               const int dist_count, DistinfTyp ftype_inf )
{
    char      *fct_name = "MakeDval";
    Distinf   *dist;
    Struc     *strucptr;
    int       count;
    float     dval;

    if ( ftype_inf != inf_normal && ftype_inf != inf_multi_model )
	FATAL( "Wrong ftype_inf value - code error" );

    dist = dist_inf;
    fprintf( fpv, "$ STRUC_COUNT: %i\n", setptr->struc_count );
    strucptr = setptr->fst_struc;
    while ( strucptr )
    {
        fprintf( fpv, "$ STRUCTURE: %i\n", strucptr->struc_num );
        fprintf( fpv, "$ BEGIN_DATA: \n" );
        count = dist_count;
        if ( ftype_inf == inf_normal )  /* restart at head of dinf list */
	    dist = dist_inf;
	                                /* else continue with next model */
        while ( count-- )
        {
            dval = SQRDIST( strucptr->coords[dist->idx_A],
                            strucptr->coords[dist->idx_B] );
            fprintf( fpv, "%f\n", sqrt( dval ) );
            dist++;
        }
        strucptr = strucptr->next_struc;
    }
}

/****************  MakeDvall  ****************/
/*
**  Compute and write an extended distance values list.
**  The file has already been opened, and the header has been written.
*/

void MakeDvall( FILE *fpv, Strucset *setptr, Distinfl *dist_infl, const int dist_count )
{
    Distinfl  *dist;
    Distidx   *A, *B;
    Struc     *strucptr;
    int       count_A, count_B, count, tot = 0;
    float     dval;

/* count the total number of distances */

    count = dist_count;
    dist = dist_infl;
    while ( count-- )
    {
	tot += ( dist->count_A * dist->count_B );
	dist++;
    }

/* now calculate and write */

    fprintf( fpv, "$ TOTAL_DIST_COUNT: %i\n", tot );
    fprintf( fpv, "$ STRUC_COUNT: %i\n", setptr->struc_count );
    strucptr = setptr->fst_struc;
    while ( strucptr )
    {
        fprintf( fpv, "$ STRUCTURE: %i\n", strucptr->struc_num );
        fprintf( fpv, "$ BEGIN_DATA: " );
        count = dist_count;
        dist = dist_infl;
        while ( count-- )
        {
	    fprintf( fpv, "\n%i", dist->count_A * dist->count_B );
	    count_A = dist->count_A;
	    A = dist->A;
	    while ( count_A-- )
	    {
	  	count_B = dist->count_B;
		B = dist->B;
		while ( count_B-- )
		{
		    dval = SQRDIST( strucptr->coords[A->idx],
				    strucptr->coords[B->idx] );
		    fprintf( fpv, " %f", sqrt( dval ) );
		    B++;
		}
		A++;
	    }
            dist++;
        }
	fputc( '\n', fpv );
        strucptr = strucptr->next_struc;
    }
}
